﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


//指针（2）
//const修饰变量的时候，叫：常变量
//这个被修饰的变量本质上还是变量，只是不能被修改
//结论：const修饰指针变量的时候
//• const如果放在* 的左边，修饰的是指针指向的内容，保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
//• const如果放在* 的右边，修饰的是指针变量本⾝，保证了指针变量的内容不能修改，但是指针指向的内容，可以通过指针改变。
//int main()
//{
//	const int num = 100;
//	num = 200;
//	printf("%d\n", num);
//
//	return 0;
//}

//int main()
//{
//	//int arr[10] = { 0 };
//
//	//const int n = 10;
//	////C99之前不支持变长数组，数组大小是需要常量，常量表达式来指定的，不能是变量
//	//int arr[n];
//
//	return 0;
//}

//int main()
//{
//	const int n = 10;
//	int* p = &n;
//	*p = 200;
//	printf("%d\n", n);
//
//	return 0;
//}



////const修饰指针变量
//int main()
//{
//	int n = 10;
//	int m = 100;
//
//	int const * p = &n;
//	//const修饰指针变量放在*左边，限制的是指针指向的内容，也就是不能通过指针变量来修改它所指向的内容
//	//但是指针变量本身是可以改变的
//
//	*p = 20;//ok
// 
// p = &m;
//
//	return 0;
//}


////const修饰指针变量
//int main()
//{
//	int n = 10;
//	int m = 100;
//
//	int * const p = &n;
//	//const放在*的右边，限制的是指针变量本身，指针指针不能改变它的指向，但是可以通过指针变量修改它所指向的内容
//
//	*p = 20;
//
//	p = &m;//ok
//
//	return 0;
//}



////const修饰指针变量
//int main()
//{
//	int n = 10;
//	int m = 100;
//
//	int const * const p = &n;
//	//*左右两边都放上const，意味着全部都被限制了
//
//	*p = 20;
//
//	p = &m;//ok
//
//	return 0;
//}





//指针的运算

//指针的基本运算有三种，分别是：
//• 指针 + -整数
//• 指针 - 指针
//• 指针的关系运算



////1.指针类型决定了指针+1的步长，决定了指针解引用的权限
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = &arr[0];
//
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", *p);
//		p++;
//
//		//printf("%d ", *(p + i));   //第二种方法
//	}
//
//	return 0;
//}



//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//
//	int i = 0;
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = &arr[sz-1];
//
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", *p);
//		p--;
//	}
//
//	return 0;
//}




//指针-指针
//指针-指针，计算的前提条件是两个指针指向同一个空间
//指针-指针的绝对值 是指针和指针之间元素的个数
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//
//	printf("%d\n", &arr[9] - &arr[0]);
//	printf("%d\n", &arr[0] - &arr[9]);
//
//	return 0;
//}



//int main()
//{
//	char ch[6] = { 0 };
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//
//	printf("%d\n",&ch[5] - &arr[0]);//err
//
//	return 0;
//}


//写一个函数，求字符串的长度
//strlen其实统计的是字符串中\0之前的个数
#include <string.h>

//方法一
//size_t my_strlen(char* p)
//{
//	int count = 0;
//	while (*p != '\0')
//	{
//		count++;
//		p++;
//	}
//	return count;
//}
//int main()
//{
//	char arr[] = "abcdef";
//	size_t len = my_strlen(arr);//数组名其实是数组首元素的地址  arr == arr[0]
//	printf("%zd\n", len);
//
//	return 0;
//}


//方法二
//size_t my_strlen(char* p)
//{
//	char* start = p;
//	char* end = p;
//	while (*end != '\0')
//	{
//		end++;
//	}
//	return end-start;
//}
//int main()
//{
//	char arr[] = "abcdef";
//	size_t len = my_strlen(arr);//数组名其实是数组首元素的地址  arr == arr[0]
//	printf("%zd\n", len);
//
//	return 0;
//}



//野指针
//概念：野指针就是指针指向的位置是不可知的（随机的、不正确的、没有明确限制的）

int main()
{
	//一个局部变量不初始化的话，它的值是随机的
	int* p;//p是局部变量，但是没有初始化，其值是随机值的，如果将p中存放的值当做地址
	      //解引用操作符就会形成非法访问
	*p = 20;//p就是野指针

	return 0;
}




//如何规避野指针

//指针初始化
//如果明确知道指针指向哪⾥就直接赋值地址，如果不知道指针应该指向哪⾥，可以给指针赋值NULL.
//NULL 是C语⾔中定义的⼀个标识符常量，值是0，0也是地址，这个地址是⽆法使⽤的，读写该地址
//会报错。
//int main()
//{
//	int a = 10;
//	int* p = &a;//给一个明确的地址
//
//	int* p2 = NULL;
//	*p2 = 200;
//
//	return 0;
//}




//⼩⼼指针越界
//⼀个程序向内存申请了哪些空间，通过指针也就只能访问哪些空间，不能超出范围访问，超出了就是越界访问。

//指针变量不再使⽤时，及时置NULL，指针使⽤之前检查有效性

//int main()
//{
//	int* p = NULL;
//	if (p != NULL)
//	{
//		*p = 20;
//	}
//	return 0;
//}


//避免返回局部变量的地址




//assert 断⾔
//assert.h 头⽂件定义了宏 assert() ，⽤于在运⾏时确保程序符合指定条件，如果不符合，就报错终⽌运⾏。这个宏常常被称为“断⾔”。

//assert() 的使⽤对程序员是⾮常友好的，使⽤ assert() 有⼏个好处：它不仅能⾃动标识⽂件和出问题的⾏号，还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。
//如果已经确认程序没有问题，不需要再做断⾔，就在 #include <assert.h> 语句的前⾯，定义⼀个宏 NDEBUG 。

//assert() 的缺点是，因为引⼊了额外的检查，增加了程序的运⾏时间。⼀般我们可以在 Debug 中使⽤，在 Release 版本中选择禁⽤ assert 就⾏，
//在 VS 这样的集成开发环境中，在 Release 版本中，直接就是优化掉了。这样在debug版本写有利于程序员排查问题，
//在 Release 版本不影响⽤⼾使⽤时程序的效率


////#define NDEBUG
//#include <assert.h>
//int main()
//{
//	int a = 10;
//	int* p = &a;
//	assert(p != NULL);//assert判断p是否为空，要包含头文件<assert.h>
//
//	return 0;
//}






//求字符串长度
//参数s指向的字符串不期望被修改
//#include <assert.h>
//size_t my_strlen(const char* s)
//{
//	size_t count = 0;
//	assert(s != NULL);//检测指针s是否有效
//	while (*s)
//	{
//		count++;
//		s++;
//	}
//	return count;
//}
//
//int main()
//{
//	char arr[] = "abcdef";
//	size_t len = my_strlen(arr);
//	printf("%zd\n", len);
//
//	return 0;
//}





//指针的传值调用和传址调用
//当实参传递给形参的时候，形参是实参的一份临时拷贝，对形参的修改不会影响实参
void Swap(int* x, int* y)
{
	int z = 0;
	z = *x;
	*x = *y;
	*y = z;
}
int main()
{

	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);

	printf("交换前：a = %d b = %d\n", a, b);
	Swap(&a, &b);

	printf("交换后：a = %d b = %d\n", a, b);

	return 0;
}